﻿using System.Threading;
using System.Windows.Forms;
using MahApps.Metro.Controls;
using MahApps.Metro.Controls.Dialogs;
using Microsoft.Tools.WindowsInstallerXml.Bootstrapper;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using Wix.Win8SetupBootstrapper.Annotations;
using Wix.Win8SetupBootstrapper.Classes;
using Wix.Win8SetupBootstrapper.Classes.Configuration;
using Wix.Win8SetupBootstrapper.Enums;
using Wix.Win8SetupBootstrapper.Views;

namespace Wix.Win8SetupBootstrapper.ViewModels
{
    public sealed class RootViewModel : INotifyPropertyChanged
    {
        private readonly RootView _rootView;
        private FrameworkElement _currentControl;

        private bool _newerVersionAlreadyInstalled;
        private PageController _pageController;
        private TransitionType _transitionType;
        private readonly IList<string> _bundlesToUpgrade;
        private BurnInstallationState _burnInstallationState;
        private string _errorMessage;

        public PageController PageController
        {
            get { return _pageController; }
        }

        public BootstrapperApplication Bootstrapper { get; set; }

        public FrameworkElement CurrentControl
        {
            get { return _currentControl; }
            set { _currentControl = value; OnPropertyChanged("CurrentControl"); }
        }

        public IntPtr Hwnd
        {
            get;
            set;
        }

        public BurnInstallationState BurnInstallationState
        {
            get { return _burnInstallationState; }
            set { _burnInstallationState = value; OnPropertyChanged("BurnInstallationState"); }
        }

        public InstallationMode InstallationMode { get; set; }

        public InstallationResult InstallationResult { get; set; }

        public int FinalResult { get; private set; }

        public Configuration Configuration { get; private set; }

        public List<PackageItemViewModel> PackagesList { get; private set; }

        public TransitionType TransitionType
        {
            get { return _transitionType; }
            set
            {
                _transitionType = value;
                OnPropertyChanged("TransitionType");
            }
        }

        public RootViewModel(RootView rootView, BootstrapperApplication bootstrapper)
        {
            _rootView = rootView;
            Bootstrapper = bootstrapper;
            _bundlesToUpgrade = new List<string>();
            TransitionType = TransitionType.Left;
            this.BurnInstallationState = BurnInstallationState.NotPresent;
            Configuration = ConfigurationLoader.Load();
            this.InitPackages();

            this.InitEvents();
        }

        private void InitEvents()
        {
            this.Bootstrapper.DetectBegin += Bootstrapper_DetectBegin;
            this.Bootstrapper.DetectComplete += Bootstrapper_DetectComplete;
            this.Bootstrapper.Error += Bootstrapper_Error;
            this.Bootstrapper.Shutdown += Bootstrapper_Shutdown;
            this.Bootstrapper.ResolveSource += Bootstrapper_ResolveSource;
            this.Bootstrapper.ApplyBegin += Bootstrapper_ApplyBegin;
            this.Bootstrapper.ApplyComplete += Bootstrapper_ApplyComplete;
            this.Bootstrapper.PlanRelatedBundle += Bootstrapper_PlanRelatedBundle;
            this.Bootstrapper.DetectRelatedBundle += Bootstrapper_DetectRelatedBundle;
        }

        void Bootstrapper_Shutdown(object sender, ShutdownEventArgs e)
        {
            if (this.RestartRequired)
                e.Result = Result.Restart;
        }

        private void InitPackages()
        {
            var bundle = BundleInfoLoader.Load();
            PackagesList = new List<PackageItemViewModel>();

            foreach (var package in bundle.Packages)
            {
                if (package.Id.Contains("x64") && !Environment.Is64BitOperatingSystem)
                {
                    continue;
                }
                if (package.Id.Contains("x86") && Environment.Is64BitOperatingSystem)
                {
                    continue;
                }

                var packageItemVm = new PackageItemViewModel
                {
                    Description = package.Description,
                    DisplayName = package.DisplayName,
                    IsSelected = true,
                    PackageId = package.Id,
                    FeaturesList = new List<FeatureItemViewModel>(),
                };

                PackagesList.Add(packageItemVm);
            }

            foreach (var feature in bundle.PackageFeatures)
            {
                var featureItem = new FeatureItemViewModel(feature.FeatureId, feature.Title, feature.Description,
                    feature.Size) {IsSelected = true};

                var package = this.PackagesList.First(e => e.PackageId == feature.PackageId);

                package.FeaturesList.Add(featureItem);
            }
        }

        void Bootstrapper_DetectRelatedBundle(object sender, DetectRelatedBundleEventArgs e)
        {
            if (e.Operation == RelatedOperation.Downgrade)
            {
                this.Bootstrapper.Engine.Log(LogLevel.Error, "newer version of product is already installed");
                this._newerVersionAlreadyInstalled = true;
            }
            if (e.Operation == RelatedOperation.MajorUpgrade || e.Operation == RelatedOperation.MinorUpdate)
            {
                this._bundlesToUpgrade.Add(e.ProductCode);
            }
        }

        void Bootstrapper_PlanRelatedBundle(object sender, PlanRelatedBundleEventArgs e)
        {
            this.Bootstrapper.Engine.Log(LogLevel.Standard,
                string.Format("Setting Bundle to Absent for processing upgrade: {0}", e.BundleId));

            foreach (var bundle in _bundlesToUpgrade)
            {
                this.Bootstrapper.Engine.Log(LogLevel.Standard, string.Format("check for upgrade: product code: {0}, bundleId = {1}", bundle, e.BundleId));
                if (string.CompareOrdinal(bundle, e.BundleId) == 0)
                {
                    this.Bootstrapper.Engine.Log(LogLevel.Standard, "setting bundle to Absent");
                    e.State = RequestState.Absent;
                }
            }
        }

        void Bootstrapper_ApplyComplete(object sender, ApplyCompleteEventArgs e)
        {
            bool lockObject = false;
            try
            {
                Monitor.Enter(this, ref lockObject);
                this.FinalResult = e.Status;
                if (this.InstallationResult == InstallationResult.Error ||
                    this.InstallationResult == InstallationResult.Canceled)
                {
                    WixBootstrapper.BootstrapperDispatcher.BeginInvoke(new Action(
                        () => this.SetCurrentPage(Pages.FinishErrorPage)));
                }
                else
                {
                    this.InstallationResult = e.Restart == ApplyRestart.RestartRequired
                        ? InstallationResult.SuccessRestartRequired
                        : InstallationResult.Success;
                    WixBootstrapper.BootstrapperDispatcher.BeginInvoke(new Action(
                        () => this.SetCurrentPage(Pages.FinishPage)));
                }
            }
            finally
            {
                if (lockObject)
                {
                    Monitor.Exit(this);
                }
            }
        }

        void Bootstrapper_ApplyBegin(object sender, ApplyBeginEventArgs e)
        {

        }

        void Bootstrapper_ResolveSource(object sender, ResolveSourceEventArgs e)
        {
            this.Bootstrapper.Engine.Log(LogLevel.Standard, string.Format("ResolveSource Event: {0}", e.DownloadSource));

            if (e.DownloadSource.Contains("http") &&
                !InternetConnectionValidator.IsInternetConnectionAvailabe(this.Bootstrapper.Engine))
            {
                this.Bootstrapper.Engine.Log(LogLevel.Error, "No internet connection available");
                var task = _rootView.ShowMessageAsync("Error", "No internet connection available");
                task.Wait();
                e.Result = Result.Cancel;
                this.IsCancelled = true;
            }
            else
            {
                e.Result = Result.Download;
            }
        }

        void Bootstrapper_Error(object sender, ErrorEventArgs e)
        {
            if (e != null)
            {
                this.InstallationResult = InstallationResult.Error;
                this.ErrorMessage = e.ErrorMessage;
            }
        }

        public bool RestartRequired { get; set; }

        public void PlanAction(LaunchAction action)
        {
            this.Bootstrapper.Engine.Plan(action);
        }

        public ICommand RepairCommand
        {
            get
            {
                return new BaseCommand(delegate
                {
                    this.PlanAction(LaunchAction.Repair);
                }, delegate { return true; });
            }
        }

        public ICommand UninstallCommand
        {
            get
            {
                return new BaseCommand(delegate
                {
                    this.PlanAction(LaunchAction.Uninstall);
                }, delegate { return true; });
            }
        }

        public string ErrorMessage
        {
            get { return _errorMessage; }
            set { _errorMessage = value; OnPropertyChanged("ErrorMessage"); }
        }

        public bool IsCancelled { get; set; }

        public void ApplyAction()
        {
            this.Bootstrapper.Engine.Apply(this.Hwnd);
        }

        void Bootstrapper_DetectComplete(object sender, DetectCompleteEventArgs e)
        {
            if (this._newerVersionAlreadyInstalled)
            {
                this.Bootstrapper.Engine.Log(LogLevel.Standard, "Downgrade process...");
                WixBootstrapper.BootstrapperDispatcher.BeginInvoke(new Action(
                    () => _rootView.ShowMessageAsync("Error", "Newer version of Product Installed")));
                WixBootstrapper.BootstrapperDispatcher.BeginInvoke(
                    new Action(WixBootstrapper.BootstrapperDispatcher.InvokeShutdown));
            }
            else
            {
                WixBootstrapper.BootstrapperDispatcher.BeginInvoke(new Action(delegate
                {
                    this.InstallationMode = InstallationMode.Install;
                    this.InitializePageSequence();
                }));
            }
        }

        private void InitializePageSequence()
        {
            this._pageController = new PageUiController(this);

            this._pageController.SetFirstPage();
        }

        private void Bootstrapper_DetectBegin(object sender, DetectBeginEventArgs e)
        {
            BurnInstallationState = e.Installed ? BurnInstallationState.Present : BurnInstallationState.NotPresent;
        }

        public event PropertyChangedEventHandler PropertyChanged;

        [NotifyPropertyChangedInvocator]
        private void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
        }

        public void RequestCancel(bool showMessage)
        {
            if (showMessage)
            {

                var task = _rootView.ShowMessageAsync("Question", "Are you sure you want to cancel installation?",
                    MessageDialogStyle.AffirmativeAndNegative, new MetroDialogSettings
                    {
                        AffirmativeButtonText = "Yes",
                        ColorScheme = MetroDialogColorScheme.Theme,
                        NegativeButtonText = "No",
                        UseAnimations = true,
                    });

                task.ContinueWith(delegate
                {
                    if (task.Result == MessageDialogResult.Affirmative)
                    {
                        this.IsCancelled = true;

                        WixBootstrapper.BootstrapperDispatcher.InvokeShutdown();
                    }
                });

            }
        }

        public void SetCurrentPage(Pages page)
        {
            if (_pageController == null)
            {
                _pageController = new PageUiController(this);
            }
            _pageController.SetPage(page);
        }

        public void SetNextPage()
        {
            _pageController.SetPage(_pageController.NextPage);
        }
    }
}